Docker Basics for Spring Boot Applications
What is Docker?
Docker is a containerization platform that packages applications and their dependencies into lightweight, portable containers. For Spring Boot applications, Docker provides consistent deployment environments across development, testing, and production.
Core Docker Concepts
Images
- Definition: Read-only templates used to create containers
- Spring Boot Context: Contains your JAR file, JVM, and runtime dependencies
- Example:
openjdk:17-jre-slim
as base image for Spring Boot apps
Containers
- Definition: Running instances of Docker images
- Spring Boot Context: Your application running in an isolated environment
- Lifecycle: Created, started, stopped, and removed
Dockerfile
- Definition: Text file with instructions to build Docker images
- Purpose: Automates the image creation process
Essential Dockerfile for Spring Boot
Basic Dockerfile Structure
# Use official OpenJDK runtime as base image
FROM openjdk:17-jre-slim
# Set working directory inside container
WORKDIR /app
# Copy the JAR file into container
COPY target/myapp-1.0.0.jar app.jar
# Expose the port Spring Boot runs on
EXPOSE 8080
# Command to run the application
ENTRYPOINT ["java", "-jar", "app.jar"]
Multi-Stage Build Dockerfile (Recommended)
# Build stage
FROM maven:3.8.4-openjdk-17 AS build
WORKDIR /app
COPY pom.xml .
COPY src ./src
RUN mvn clean package -DskipTests
# Runtime stage
FROM openjdk:17-jre-slim
WORKDIR /app
COPY --from=build /app/target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
Optimized Dockerfile with Layers
FROM openjdk:17-jre-slim
# Create non-root user for security
RUN groupadd -r spring && useradd -r -g spring spring
WORKDIR /app
# Copy dependencies first (better caching)
COPY target/dependency/ ./
COPY target/classes ./classes/
COPY target/*.jar app.jar
# Change ownership to spring user
RUN chown -R spring:spring /app
USER spring
EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=3s --start-period=60s --retries=3 \
CMD curl -f http://localhost:8080/actuator/health || exit 1
ENTRYPOINT ["java", "-cp", "/app/classes:/app/dependency/*", "com.example.Application"]
Essential Docker Commands
Image Operations
# Build image from Dockerfile
docker build -t myapp:latest .
# List images
docker images
# Remove image
docker rmi myapp:latest
# Pull image from registry
docker pull openjdk:17-jre-slim
Container Operations
# Run container
docker run -p 8080:8080 myapp:latest
# Run in detached mode
docker run -d -p 8080:8080 --name myapp-container myapp:latest
# List running containers
docker ps
# List all containers
docker ps -a
# Stop container
docker stop myapp-container
# Remove container
docker rm myapp-container
# Execute command in running container
docker exec -it myapp-container bash
Development Commands
# Run with environment variables
docker run -p 8080:8080 -e SPRING_PROFILES_ACTIVE=dev myapp:latest
# Mount volume for logs
docker run -p 8080:8080 -v $(pwd)/logs:/app/logs myapp:latest
# Run with network
docker run -p 8080:8080 --network mynetwork myapp:latest
Docker Compose for Spring Boot
Basic docker-compose.yml
version: '3.8'
services:
app:
build: .
ports:
- "8080:8080"
environment:
- SPRING_PROFILES_ACTIVE=docker
depends_on:
- database
networks:
- app-network
database:
image: postgres:13
environment:
POSTGRES_DB: myapp
POSTGRES_USER: user
POSTGRES_PASSWORD: password
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
networks:
- app-network
volumes:
postgres_data:
networks:
app-network:
driver: bridge
Development-Focused docker-compose.yml
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile.dev
ports:
- "8080:8080"
- "5005:5005" # Debug port
environment:
- SPRING_PROFILES_ACTIVE=dev
- JAVA_TOOL_OPTIONS=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005
volumes:
- .:/app
- /app/target
depends_on:
- database
- redis
database:
image: postgres:13
environment:
POSTGRES_DB: myapp_dev
POSTGRES_USER: dev
POSTGRES_PASSWORD: dev123
ports:
- "5432:5432"
volumes:
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
redis:
image: redis:7-alpine
ports:
- "6379:6379"
Spring Boot Docker Integration
Application Properties for Docker
# application-docker.properties
spring.datasource.url=jdbc:postgresql://database:5432/myapp
spring.datasource.username=${DB_USER:user}
spring.datasource.password=${DB_PASSWORD:password}
# Redis configuration
spring.redis.host=redis
spring.redis.port=6379
# Actuator for health checks
management.endpoints.web.exposure.include=health,info
management.endpoint.health.show-details=always
Maven Configuration
Add to pom.xml
:
<properties>
<docker.image.name>myapp</docker.image.name>
</properties>
<build>
<plugins>
<!-- Spring Boot Maven Plugin -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<image>
<name>${docker.image.name}:${project.version}</name>
</image>
</configuration>
</plugin>
<!-- Docker Maven Plugin -->
<plugin>
<groupId>com.spotify</groupId>
<artifactId>dockerfile-maven-plugin</artifactId>
<version>1.4.13</version>
<configuration>
<repository>${docker.image.name}</repository>
<tag>${project.version}</tag>
</configuration>
</plugin>
</plugins>
</build>
Best Practices for Spring Boot + Docker
1. Security
# Use non-root user
RUN addgroup --system spring && adduser --system spring --ingroup spring
USER spring:spring
2. Health Checks
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s \
CMD curl -f http://localhost:8080/actuator/health || exit 1
3. Efficient Layering
# Copy dependencies separately for better caching
COPY target/dependency/ ./dependency/
COPY target/classes ./classes/
COPY target/*.jar app.jar
4. Environment-Specific Images
# Development
docker build -f Dockerfile.dev -t myapp:dev .
# Production
docker build -f Dockerfile.prod -t myapp:prod .
Common Docker Commands for Development
Quick Development Workflow
# Build and run
docker build -t myapp . && docker run -p 8080:8080 myapp
# Using docker-compose
docker-compose up --build
# Rebuild and restart specific service
docker-compose up --build app
# View logs
docker-compose logs -f app
# Clean up
docker-compose down
docker system prune -f
Debugging
# Run with debug port exposed
docker run -p 8080:8080 -p 5005:5005 -e JAVA_TOOL_OPTIONS="-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005" myapp
# Access container shell
docker exec -it container-name bash
# Check logs
docker logs container-name -f
Docker Networking for Spring Boot
Creating Networks
# Create custom network
docker network create myapp-network
# Run containers on same network
docker run --network myapp-network --name database postgres:13
docker run --network myapp-network -p 8080:8080 myapp
Service Discovery
In docker-compose, services can communicate using service names:
spring.datasource.url=jdbc:postgresql://database:5432/myapp
Volume Management
Persistent Data
# Create named volume
docker volume create myapp-data
# Use volume
docker run -v myapp-data:/app/data myapp
Development Volumes
# Mount source code for hot reload
docker run -v $(pwd):/app -p 8080:8080 myapp:dev
Deployment Considerations
Production Dockerfile
FROM openjdk:17-jre-slim
# Security updates
RUN apt-get update && apt-get upgrade -y && rm -rf /var/lib/apt/lists/*
# Non-root user
RUN groupadd -r spring && useradd -r -g spring spring
WORKDIR /app
COPY --chown=spring:spring target/*.jar app.jar
USER spring
EXPOSE 8080
# Production JVM options
ENV JAVA_OPTS="-Xms512m -Xmx1024m -XX:+UseG1GC"
ENTRYPOINT exec java $JAVA_OPTS -jar app.jar
Environment Variables
# Production deployment
docker run -d \
-p 8080:8080 \
-e SPRING_PROFILES_ACTIVE=prod \
-e DB_HOST=prod-db-host \
-e DB_PASSWORD=secure-password \
--name myapp-prod \
myapp:latest
This guide covers the essential Docker concepts and practical examples needed for developing and deploying Spring Boot applications effectively.